/**
 * @author  Martin Knobloch / pexx
 *          ________________________ 
 *          IIm09 HS-Zittau/Görlitz	|
 *          me@pexx.org 			|
 *          http://www.pexx.org	    |
 *
 */
package org.pexx.rdm.data;

import java.io.IOException;
import java.io.RandomAccessFile;

import org.pexx.components.logger.Log;


/**
 * 
 */
public class DataReader implements Runnable {
	
	private static final long MAX_FILE_LEN = 1024 * 1024 * 100; // max 100mb

	private static int threadId = 0;

	private String fileName;
	private DataReceiver reciver;

	private boolean running;
	private RandomAccessFile raf;
	private int defLineLen = 300;
	private long sleep = 1000;
	private boolean parseAll = false;
	private Parser parser;
	private long pos = 0;
	private long length;

	private boolean autoclear = true;
	
	/**
	 * @param file2 
	 * 
	 */
	public DataReader(String file, Parser parser, DataReceiver reciver) {

		this.fileName = file;
		this.parser = parser;
		this.reciver = reciver;
		
	}
	
	
	/**
	 * @return the pos
	 */
	public long getPos() {

		return pos;
	}
	
	/**
	 * same implementation as in RandomAccessFile.readLine() but converts some UTF8chars
	 * @return
	 * @throws IOException
	 */
	private final String readLine () throws IOException {
		
		StringBuffer input = new StringBuffer();
		int c = -1;
		boolean eol = false;

		while (!eol) {
		    switch (c = raf.read()) {
		    	
	    	case -1:
	    	case '\n':
	    			eol = true;
	    			break;
	    			
	    	case '\r':
	    		eol = true;
	    		long cur = raf.getFilePointer();
	    		if ((raf.read()) != '\n') {
	    			raf.seek(cur);
	    		}
	    		break;
	    		
	    	case 0xC3 :
	    		switch (raf.read()) {
	    			case 0x84 : 
	    				input.append('Ä');
	    				break;
	    				
	    			case 0x96 : 
	    				input.append('Ö');
	    				break;
	    				
	    			case 0x9C : 
	    				input.append('Ü');
	    				break;
	    				
	    			case 0xA4 : 
	    				input.append('ä');
	    				break;
	    				
	    			case 0xB6 : 
	    				input.append('ö');
	    				break;

	    			case 0xBC : 
	    				input.append('ü');
	    				break;
	    				
	    			case 0x9F : 
	    				input.append('ß');
	    				break;	    					    			
	    		}
	    		
	    		break;
	    		
	    		
		    default:    		    	
		    	
				input.append((char)c);
				break;
		    }
		}

		if ((c == -1) && (input.length() == 0)) {
		    return null;
		}
		return input.toString();
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
	@Override
	public void run() {
		
		int t = threadId++;
	
		running = true;
		String line;		
		DataSet tset;

		Log.getLog().log("DataReader [" + t + "] started.");
		
		
		while (running) {					// working loop

			try {								

				raf.seek(pos);									// goto previous end pos
				this.length = raf.length();
				
				long c = 0;
				
				while (running && ((line = readLine()) != null)) {		// parse and collect new lines since last loop
					if(!line.isEmpty() && ((tset = parser.parse(line)) != null))	// skip erronous datalines
						reciver.collect(tset);

					pos  = raf.getFilePointer();						// store new position	
					
					if (c++ % 100 == 0) {		// if reading more then 100 lines add a little sleeptime to cooldown ur cpu ^^
						Thread.sleep(1);
					}

				}
				if (autoclear && (raf.length() > MAX_FILE_LEN)) {
					raf.setLength(0);					
					this.length = 0;
					this.pos = 0;
				}
				
				// end reached
				
				Thread.sleep(sleep);
				
			} catch (IOException e1) {
				error (e1);
			} catch (InterruptedException e) {}
			
		}
		
		Log.getLog().log("DataReader [" + t + "] stopped.");
		
		pos = -1;
		
		try {
			raf.close();
		} catch (IOException e) {
			error (e);
		}
		
	}

	
	/**
	 * @param e1
	 */
	private void error(Exception e) {

		Log.getLog().log(e);
		
	}

	public void start () throws IOException {
		
		pos = 0;
		aqquire ();
		
		new Thread(this).start();
	}

	/**
	 * @throws IOException 
	 * 
	 */
	private void aqquire() throws IOException {

		raf = new RandomAccessFile(fileName, "rw");
		
		if (!parseAll) {	
			pos = raf.length();
			
			if (pos > defLineLen) {		// file allready has one or more lines
				pos -= defLineLen ;		// set pointer to previous position				
				raf.seek(pos);							
				raf.readLine();			// consume until lineend
				pos = raf.length();		// set pos to lineend
			}
		}			
	}

	public void stop () {
		running = false;
	}
	
	
	/**
	 * @param sleep the sleep to set
	 */
	public void setSleep(long sleep) {

		this.sleep = sleep;
	}
	
	
	/**
	 * @return the sleep
	 */
	public long getSleep() {

		return sleep;
	}

	
	/**
	 * @return the parseAll
	 */
	public boolean isParseAll() {

		return parseAll;
	}
	
	/**
	 * @param parseAll the parseAll to set
	 */
	public void setParseAll(boolean parseAll) {

		this.parseAll = parseAll;
	}
	
	
	/**
	 * @param defLineLen the defLineLen to set
	 */
	public void setDefLineLen(int defLineLen) {

		this.defLineLen = defLineLen;
	}
	
	/**
	 * @return the parser
	 */
	public Parser getParser() {

		return parser;
	}
	
	
	/**
	 * @return the length
	 */
	public long getLength() {

		return length;
	}
	
	
	/**
	 * @return the running
	 */
	public boolean isRunning() {

		return running;
	}
	
	
	/**
	 * @return the autoclear
	 */
	public boolean isAutoclear() {

		return autoclear;
	}
	
	/**
	 * @param autoclear the autoclear to set
	 */
	public void setAutoclear(boolean autoclear) {

		this.autoclear = autoclear;
	}

}
